#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
npm audit 插件
用于检查 npm 依赖的安全漏洞
"""

import os
import subprocess
import json
from typing import Dict, List, Any


class NpmAuditPlugin:
    """npm audit 插件"""
    
    def __init__(self):
        self.name = "npm-audit"
        self.description = "npm 依赖安全检查"
    
    def execute(self, config: Dict[str, Any], context: Dict[str, Any]) -> Dict[str, Any]:
        """执行 npm audit 检查"""
        try:
            # 检查是否存在 package.json
            if not os.path.exists('package.json'):
                return {
                    'success': True,
                    'output': '未找到 package.json，跳过 npm audit 检查',
                    'exit_code': 0
                }
            
            # 获取配置参数
            audit_level = config.get('audit_level', 'moderate')
            fix = config.get('fix', False)
            production_only = config.get('production_only', False)
            
            # 构建命令
            cmd = ['npm', 'audit']
            
            # 添加审计级别
            if audit_level:
                cmd.extend(['--audit-level', audit_level])
            
            # 生产环境检查
            if production_only:
                cmd.append('--production')
            
            # 执行命令
            result = subprocess.run(
                cmd,
                capture_output=True,
                text=True,
                timeout=300
            )
            
            # 解析结果
            output_lines = result.stdout.split('\n') if result.stdout else []
            error_lines = result.stderr.split('\n') if result.stderr else []
            
            # 检查是否有漏洞
            has_vulnerabilities = result.returncode != 0
            
            if has_vulnerabilities:
                # 尝试自动修复
                if fix and self._can_auto_fix():
                    fix_result = self._auto_fix()
                    if fix_result['success']:
                        return {
                            'success': True,
                            'output': '发现安全漏洞，已自动修复',
                            'exit_code': 0
                        }
                
                # 格式化输出
                formatted_output = self._format_audit_output(output_lines, error_lines)
                return {
                    'success': False,
                    'output': formatted_output,
                    'error': '\n'.join(error_lines),
                    'exit_code': result.returncode
                }
            else:
                return {
                    'success': True,
                    'output': 'npm audit 检查通过，未发现安全漏洞',
                    'exit_code': 0
                }
                
        except subprocess.TimeoutExpired:
            return {
                'success': False,
                'output': '',
                'error': 'npm audit 执行超时',
                'exit_code': 1
            }
        except Exception as e:
            return {
                'success': False,
                'output': '',
                'error': f'npm audit 执行失败: {e}',
                'exit_code': 1
            }
    
    def _format_audit_output(self, output_lines: List[str], error_lines: List[str]) -> str:
        """格式化 npm audit 输出"""
        formatted = []
        formatted.append("发现安全漏洞:")
        formatted.append("=" * 50)
        
        # 解析漏洞信息
        vulnerabilities = self._parse_vulnerabilities(output_lines)
        
        for vuln in vulnerabilities:
            formatted.append(f"漏洞: {vuln.get('name', 'Unknown')}")
            formatted.append(f"  严重程度: {vuln.get('severity', 'Unknown')}")
            formatted.append(f"  路径: {vuln.get('path', 'Unknown')}")
            formatted.append(f"  描述: {vuln.get('description', 'No description')}")
            if vuln.get('fix_available'):
                formatted.append(f"  修复建议: {vuln.get('fix_available', 'No fix available')}")
            formatted.append("")
        
        # 添加修复建议
        formatted.append("修复建议:")
        formatted.append("1. 运行 'npm audit fix' 自动修复可修复的漏洞")
        formatted.append("2. 运行 'npm audit fix --force' 强制修复（可能破坏兼容性）")
        formatted.append("3. 手动更新有漏洞的依赖包")
        
        return '\n'.join(formatted)
    
    def _parse_vulnerabilities(self, output_lines: List[str]) -> List[Dict[str, Any]]:
        """解析漏洞信息"""
        vulnerabilities = []
        current_vuln = {}
        
        for line in output_lines:
            line = line.strip()
            if not line:
                continue
            
            # 检测漏洞开始
            if 'vulnerabilities' in line.lower() or 'found' in line.lower():
                continue
            
            # 解析漏洞信息
            if line.startswith('Package:'):
                if current_vuln:
                    vulnerabilities.append(current_vuln)
                current_vuln = {'name': line.split(':', 1)[1].strip()}
            elif line.startswith('Severity:'):
                current_vuln['severity'] = line.split(':', 1)[1].strip()
            elif line.startswith('Path:'):
                current_vuln['path'] = line.split(':', 1)[1].strip()
            elif line.startswith('Description:'):
                current_vuln['description'] = line.split(':', 1)[1].strip()
            elif line.startswith('Fix available:'):
                current_vuln['fix_available'] = line.split(':', 1)[1].strip()
        
        if current_vuln:
            vulnerabilities.append(current_vuln)
        
        return vulnerabilities
    
    def _can_auto_fix(self) -> bool:
        """检查是否可以自动修复"""
        try:
            result = subprocess.run(
                ['npm', 'audit', '--json'],
                capture_output=True,
                text=True,
                timeout=30
            )
            
            if result.returncode == 0:
                return False
            
            audit_data = json.loads(result.stdout)
            return audit_data.get('metadata', {}).get('vulnerabilities', {}).get('fixable', 0) > 0
            
        except Exception:
            return False
    
    def _auto_fix(self) -> Dict[str, Any]:
        """自动修复漏洞"""
        try:
            # 尝试自动修复
            result = subprocess.run(
                ['npm', 'audit', 'fix'],
                capture_output=True,
                text=True,
                timeout=300
            )
            
            if result.returncode == 0:
                return {
                    'success': True,
                    'output': '自动修复成功',
                    'exit_code': 0
                }
            else:
                return {
                    'success': False,
                    'output': result.stdout,
                    'error': result.stderr,
                    'exit_code': result.returncode
                }
                
        except subprocess.TimeoutExpired:
            return {
                'success': False,
                'output': '',
                'error': '自动修复超时',
                'exit_code': 1
            }
        except Exception as e:
            return {
                'success': False,
                'output': '',
                'error': f'自动修复失败: {e}',
                'exit_code': 1
            }
    
    def check_installation(self) -> bool:
        """检查 npm 是否已安装"""
        try:
            result = subprocess.run(
                ['npm', '--version'],
                capture_output=True,
                text=True,
                timeout=10
            )
            return result.returncode == 0
        except Exception:
            return False
    
    def get_audit_summary(self) -> Dict[str, Any]:
        """获取审计摘要"""
        try:
            result = subprocess.run(
                ['npm', 'audit', '--json'],
                capture_output=True,
                text=True,
                timeout=30
            )
            
            if result.returncode == 0:
                return {
                    'success': True,
                    'vulnerabilities': 0,
                    'summary': 'No vulnerabilities found'
                }
            
            audit_data = json.loads(result.stdout)
            metadata = audit_data.get('metadata', {})
            vulnerabilities = metadata.get('vulnerabilities', {})
            
            return {
                'success': False,
                'vulnerabilities': vulnerabilities.get('total', 0),
                'critical': vulnerabilities.get('critical', 0),
                'high': vulnerabilities.get('high', 0),
                'moderate': vulnerabilities.get('moderate', 0),
                'low': vulnerabilities.get('low', 0),
                'fixable': vulnerabilities.get('fixable', 0)
            }
            
        except Exception as e:
            return {
                'success': False,
                'error': f'获取审计摘要失败: {e}'
            }


# 插件接口
def execute(config: Dict[str, Any], context: Dict[str, Any]) -> Dict[str, Any]:
    """插件执行接口"""
    plugin = NpmAuditPlugin()
    return plugin.execute(config, context)


if __name__ == "__main__":
    # 测试插件
    plugin = NpmAuditPlugin()
    
    # 测试配置
    test_config = {
        'audit_level': 'moderate',
        'fix': False,
        'production_only': False
    }
    
    test_context = {
        'staged_files': ['package.json']
    }
    
    result = plugin.execute(test_config, test_context)
    print(f"执行结果: {result}")
